// TradeSpi.cpp: implementation of the CTradeSpi class.
//
//////////////////////////////////////////////////////////////////////

#include "TradeSpi.h"
#include "msgs.h"
#include "pthread.h"
#include "BlowFish.h"
#include <sys/time.h>
#include <limits.h>

#include "mylogger.h"

double MIN_TIMER_INTERVAL=0.855;    // in ms

IFutureAccount* CreateAccount(const char *pszConfigFile){
	CTradeSpi* pSpi = new CTradeSpi(pszConfigFile);
	CCtpFutureAccount* pAcct = new CCtpFutureAccount();
	pAcct->m_pTradeSpi = pSpi;
	pSpi->m_pFutureAccount = pAcct;
	return pAcct;
}


/////////////////////////////////////////////////////////////////////
//CFClient Members;
/////////////////////////////////////////////////////////////////////
CFClient::CFClient(IPolicy* pPolicy,CTradeSpi* pTradeSpi,bool bIsSHFE,char cHedgeType){
	m_pPolicy = pPolicy;
	m_bIsSHFE = bIsSHFE;
	m_cHedgeType = cHedgeType;
	m_pTradeSpi = pTradeSpi;

	if (pTradeSpi != NULL)
		pTradeSpi->AddPolicy(pPolicy);

	if (!exists_logger())
		init_logger();
}

void CFClient::RegisterTimer(int nMillSecondsAfter,int nAction,OrderField* pOrder){
	if(m_pPolicy != NULL){
		m_pTradeSpi->RegisterTimer((CtpOrderField*)pOrder,nAction,nMillSecondsAfter,m_pPolicy);
	}
}

void CFClient::Release(){
	delete this;
}

void CFClient::LogError(const char* pszError){
	m_pTradeSpi->Error(pszError);
}

void CFClient::LogInfo(const char *pszError){
	m_pTradeSpi->Info(pszError);
}

void CFClient::LogDebug(const char *pszError){
	m_pTradeSpi->Debug(pszError);
}

void CFClient::SetSHFE( bool bSHFE )
{
	m_bIsSHFE = bSHFE;
}

OrderField* CFClient::Buy( const char* InstrumentID,int nVolume,double fPrice )
{
	return m_pTradeSpi->Buy(InstrumentID, nVolume, fPrice, m_pPolicy);
}

OrderField* CFClient::Sell( const char* InstrumentID,int nVolume,double fPrice,int closeTd )
{
	//return m_pTradeSpi->PlaceOrder(InstrumentID, USTP_FTDC_D_Sell, USTP_FTDC_OF_CloseYesterday, nVolume, fPrice);
	if (closeTd == 0)
	{
		return m_pTradeSpi->PlaceOrder(InstrumentID, THOST_FTDC_D_Sell, THOST_FTDC_OF_Close, nVolume, fPrice, m_pPolicy);
	}
	else if (closeTd == 1)
	{
		return m_pTradeSpi->PlaceOrder(InstrumentID, THOST_FTDC_D_Sell, THOST_FTDC_OF_CloseToday, nVolume, fPrice, m_pPolicy);
	}
	else
	{
		// closeTd == 2
		return m_pTradeSpi->PlaceOrder(InstrumentID, THOST_FTDC_D_Sell, THOST_FTDC_OF_CloseYesterday, nVolume, fPrice, m_pPolicy);
	}
}

OrderField* CFClient::Short( const char* InstrumentID,int nVolume,double fPrice )
{
	return m_pTradeSpi->Short(InstrumentID, nVolume, fPrice, m_pPolicy);
}

OrderField* CFClient::Cover( const char* InstrumentID,int nVolume,double fPrice,int closeTd )
{
	//return m_pTradeSpi->PlaceOrder(InstrumentID, USTP_FTDC_D_Buy, USTP_FTDC_OF_CloseYesterday, nVolume, fPrice);
	if (closeTd == 0)
	{
		return m_pTradeSpi->PlaceOrder(InstrumentID, THOST_FTDC_D_Buy, THOST_FTDC_OF_Close, nVolume, fPrice, m_pPolicy);
	}
	else if (closeTd == 1)
	{
		return m_pTradeSpi->PlaceOrder(InstrumentID, THOST_FTDC_D_Buy, THOST_FTDC_OF_CloseToday, nVolume, fPrice, m_pPolicy);
	}
	else
	{
		// closeTd == 2
		return m_pTradeSpi->PlaceOrder(InstrumentID, THOST_FTDC_D_Buy, THOST_FTDC_OF_CloseYesterday, nVolume, fPrice, m_pPolicy);
	}
}


//////////////////////////////////////////////////////////////////////
// CTradeSpi members;
//////////////////////////////////////////////////////////////////////

void* hook(void* args)
{
	//myinfo("start on timer thread.");
	TimerArgs* arg = (TimerArgs*)args;
	IPolicy* policy = arg->policy;
	int millisec = arg->millisec;
	OrderField* pOrder = arg->pOrder;
	//myinfo("start to delay " << millisec << " milliseconds.");
	//struct timeval time_begin, time_end;
	//gettimeofday(&time_begin, NULL);
	//myinfo("begin time : " << time_begin.tv_usec);
	usleep(millisec * 1000);
	//gettimeofday(&time_end, NULL);
	//myinfo("end time : " << time_end.tv_usec);
	// TODO: use lData == 1
	policy->OnTimer(pOrder, 1);
}

CTradeSpi::CTradeSpi(const char *pszInitFile):m_Config(pszInitFile)
{
    SPDLOG_INFO("CTradeSpi Construct.");
	m_connected = false;
	b_authenticated = false;
	b_position = false;
	b_instruments = false;
	m_pTradeApi = CThostFtdcTraderApi::CreateFtdcTraderApi();
	string frontAddr = m_Config.m_TradeServers["default"]->front();
	char frontIP[100];
	strcpy(frontIP, frontAddr.c_str());
	SPDLOG_INFO("frontIP = {}", frontIP);
	m_pTradeApi->RegisterFront(frontIP);
	m_pTradeApi->SubscribePrivateTopic(THOST_TERT_QUICK);
	m_pTradeApi->SubscribePublicTopic(THOST_TERT_QUICK);
	m_pTradeApi->RegisterSpi(this);
	m_pTradeApi->Init();

    //std::this_thread::sleep_for(std::chrono::seconds(10));
// 	if (!exists_logger())
// 		init_logger();
}

CTradeSpi::~CTradeSpi()
{

}

int CTradeSpi::GetLastErrorID(){
	return m_nLastError;
}

char* CTradeSpi::GetLastErrorMsg(){
	return m_cLastError;
}

void CTradeSpi::Log(const char *pszLog,int nLevel,long ThreadID){

}

void CTradeSpi::CloseLog(){
	if(m_pfLog != NULL){
		fclose(m_pfLog);
		m_pfLog = NULL;
	}
}

void CTradeSpi::Debug(const char *pszLog){
	PostLogMessage(pszLog,0);
}

void CTradeSpi::Info(const char *pszLog){
	PostLogMessage(pszLog,1);
}

void CTradeSpi::Error(const char *pszLog){
	PostLogMessage(pszLog,2);
}

void CTradeSpi::PostLogMessage(const char *pszLog,int nLevel){

}

IFutureTrader* CTradeSpi::CreateTrader(IPolicy* pPolicy,char cHedgeFlag,bool IsSHFE){
	CFClient* pClient =  new CFClient(pPolicy,this,IsSHFE,cHedgeFlag);
	pClient->m_pFutureAcct = m_pFutureAccount;
	m_ClientsLock.Lock();
	m_Clients.push_back(pClient);
	m_ClientsLock.Unlock();
	return pClient;
}


int CTradeSpi::ReqUserLogin(){
	CThostFtdcReqUserLoginField req;
	memset(&req, 0, sizeof(req));

	strcpy(req.BrokerID, m_Config.GetBroker());
	strcpy(req.UserID, m_Config.GetUser());
	strcpy(req.Password, m_Config.GetPassword());
	//strcpy(req.UserProductInfo,pszProductInfo);
	return m_pTradeApi->ReqUserLogin(&req, m_Config.GetNextRequestID());
}

void CTradeSpi::ReqUserInvestor()
{
	CThostFtdcQryInvestorField field;
	memset(&field, 0, sizeof(CThostFtdcQryInvestorField));
	strcpy(field.BrokerID, m_Config.GetBroker());
	strcpy(field.InvestorID, m_Config.GetUser());
	m_pTradeApi->ReqQryInvestor(&field, m_Config.GetNextRequestID());
}

int  CTradeSpi::ReqUserInvestor_() {

	CThostFtdcQryInvestorField field;
	memset(&field, 0, sizeof(CThostFtdcQryInvestorField));
	strcpy(field.BrokerID, m_Config.GetBroker());
	strcpy(field.InvestorID, m_Config.GetUser());
	return m_pTradeApi->ReqQryInvestor(&field, m_Config.GetNextRequestID());
}


void CTradeSpi::OnRspQryInvestor(CThostFtdcInvestorField *pRspUserInvestor, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
{
	if (pRspInfo == NULL || pRspInfo->ErrorID == 0)
	{
        SPDLOG_INFO("Query InvestorID OK.  InvestorID : {}", pRspUserInvestor->InvestorID);
		m_Config.SetInvestorID(pRspUserInvestor->InvestorID);

		req_qry_instrument();
	}
	else
	{
		SPDLOG_ERROR("Query InvestorID Error. ErrorID : {}, ErrorMsg : {}", pRspInfo->ErrorID, pRspInfo->ErrorMsg);
	}
}

void CTradeSpi::OnRspUserLogin(CThostFtdcRspUserLoginField *pRspUserLogin, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast) {
	if (pRspInfo == NULL || pRspInfo->ErrorID == 0)
	{
        SPDLOG_INFO("UserLogin Res. TradingDay : {}, MaxLocalID : {}", pRspUserLogin->TradingDay, pRspUserLogin->MaxOrderRef);
		long maxOrder = atoi(pRspUserLogin->MaxOrderRef);
		//myinfo("Start Local ID : " << maxOrder);
		if (maxOrder < 90000)
			maxOrder = 90001;
        SPDLOG_INFO("Start Local ID : {}", maxOrder);
		m_Config.SetMaxOrderRef(maxOrder);
		m_Config.SetTradingDay(pRspUserLogin->TradingDay);

		// TODO: mock send order
		//Buy("IF1608", 1, 2981);
		//CtpOrderField field;
		//memset(&field, 0, sizeof(CtpOrderField));
		//strcpy(field.BrokerID, m_Config.GetBroker());
		//strcpy(field.UserID, m_Config.GetUser());
		//sprintf(field.OrderSysID, "%12d", 261958);
		//field.OrderStatus = '3';
		//CancelOrder(&field);

		//ReqUserInvestor();


        int nTried=0;
        bool bRet = this->req_settlement_confirm();
        while(!bRet && nTried++<3){
            sleep(1);
            bRet = this->req_settlement_confirm();
            SPDLOG_INFO("req_settlement_confirm  retry : {}", nTried);
        }
    }
	else
	{
		SPDLOG_ERROR("UserLogin Error. ErrorID : {}, ErrorMsg : {}", pRspInfo->ErrorID, pRspInfo->ErrorMsg);
	}
}


bool CTradeSpi::req_settlement_confirm() {
	CThostFtdcSettlementInfoConfirmField req = {};
	strcpy(req.InvestorID, m_Config.GetUser());
	strcpy(req.BrokerID, m_Config.GetBroker());
	int rtn = m_pTradeApi->ReqSettlementInfoConfirm(&req, m_Config.GetNextRequestID());
	return rtn == 0;
}

void CTradeSpi::OnRspSettlementInfoConfirm(CThostFtdcSettlementInfoConfirmField *pSettlementInfoConfirm,
								CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
{

	SPDLOG_INFO("OnRspSettlementInfoConfirm. ErrorID: {}", pRspInfo->ErrorID);
	if (nullptr == pRspInfo || 0 == pRspInfo->ErrorID) {
		int nTried=0;
		int nRet = this->ReqUserInvestor_();
		while(nRet !=0 && nTried++<3){
			sleep(1);
			nRet = this->ReqUserInvestor_();
			SPDLOG_INFO("ReqUserInvestor_  retry : {}", nTried);
		}
	}else{
		SPDLOG_ERROR("OnRspSettlementInfoConfirm. ErrorID: {}", pRspInfo->ErrorID);
	}
    //ReqUserInvestor often cause error.  error ID=90
    //req_qry_instrument();
}

void CTradeSpi::OnFrontDisconnected(int nReason){

    SPDLOG_ERROR("OnFrontDisconnected: {}", nReason);
	char cLog[512];
	sprintf(cLog,"server disconnected,reason: %d",nReason);
	Error(cLog);
}

void CTradeSpi::OnRspError(CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
{
	SPDLOG_ERROR("OnRspError Called. ErrorID : {}, ErrorMsg : {}", pRspInfo->ErrorID, pRspInfo->ErrorMsg);
}

bool CTradeSpi::IsSHFE(const char *pszCode){

}

void CTradeSpi::FrozenPosition(CtpOrderField* pOrder,const char *pszCode,char cBuySell,char cClose,int nVolume){

}

void CTradeSpi::UnfrozenPosition(CtpOrderField* pField){

}

int CTradeSpi::Login(const char* UserID,const char* Password,char* ErrorMsg,int* ErrorID){
	int nErrorID =  Login(UserID,Password);
	if(nErrorID !=0){
		strcpy(ErrorMsg,m_cLastError);
		*ErrorID =m_nLastError;
	}else{
		*ErrorID=0;
		ErrorMsg[0]='\0';
	}
	return nErrorID;
}


int CTradeSpi::ReqAuthenticate()
{
    SPDLOG_INFO("ReqAuthenticate. GetAppID: {}, GetUser: {}", m_Config.GetAppID(), m_Config.GetUser());
	CThostFtdcReqAuthenticateField a = {0};
    strncpy(a.BrokerID, m_Config.GetBroker(), sizeof(TThostFtdcBrokerIDType));
    strncpy(a.UserID, m_Config.GetUser(), sizeof(TThostFtdcUserIDType));
    strcpy(a.AppID, m_Config.GetAppID());
    strcpy(a.AuthCode, m_Config.GetAuthCode());
    int ret = m_pTradeApi->ReqAuthenticate(&a, m_Config.GetNextRequestID());
		
	SPDLOG_INFO("ReqAuthenticate : {}", ret);
	return ret;
}


void CTradeSpi::OnRspAuthenticate(CThostFtdcRspAuthenticateField *pRspAuthenticateField, CThostFtdcRspInfoField *pRspInfo,
                              int nRequestID, bool bIsLast) {
    if (nullptr == pRspInfo || 0 == pRspInfo->ErrorID) {
		SPDLOG_INFO("OnRspDSUserCertification.");
		b_authenticated = true;

        int nTried=0;
        int nRet = this->ReqUserLogin();
        while(nRet !=0 && nTried++<3){
            sleep(1);
            nRet = this->ReqUserLogin();
            SPDLOG_INFO("ReqUserLogin  retry : {}", nTried);
        }

    } else {
        std::cout << "OnRspAuthenticate error:"
                  << pRspInfo->ErrorID
                  << pRspInfo->ErrorMsg
                  << std::endl;
    }
}


int CTradeSpi::Login(const char *pszUserID,const char *pszPassword){
	// wait for login
    SPDLOG_INFO("wait for connected.");
	while (!m_connected)
	{
		sleep(1);
        SPDLOG_INFO("sleep for connected.");
	}

    SPDLOG_INFO("wait for authenticated.");
	while (!b_authenticated)
	{
		sleep(1);
        SPDLOG_INFO("sleep for connected.");
	}


    SPDLOG_INFO("wait for b_instruments.");
    while (!b_instruments)
    {
        sleep(1);
        SPDLOG_INFO("sleep for b_instruments.");
    }


    SPDLOG_INFO("wait for b_position.");
    while (!b_position)
    {
        sleep(1);
        SPDLOG_INFO("sleep for b_position.");
    }

	CThostFtdcReqUserLoginField req;
	memset(&req, 0, sizeof(req));
	m_Config.SetUser(pszUserID);
	m_Config.SetPassword(pszPassword);
	strcpy(req.BrokerID, m_Config.GetBroker());
	strcpy(req.UserID, pszUserID);
	strcpy(req.Password, pszPassword);
	//strcpy(req.UserProductInfo,pszProductInfo);
	return m_pTradeApi->ReqUserLogin(&req, m_Config.GetNextRequestID());
}

bool CTradeSpi::req_position()
{
    SPDLOG_INFO("req_position.");
	long_pos_map_.clear();
	short_pos_map_.clear();
	CThostFtdcQryInvestorPositionField req = {};
	strcpy(req.BrokerID, m_Config.GetBroker());
	strcpy(req.InvestorID, m_Config.GetInvestorID());

    int nTried=0;
    int nRet = m_pTradeApi->ReqQryInvestorPosition(&req, m_Config.GetNextRequestID());
    while(nRet !=0 && nTried++<3){
        sleep(1);
        nRet = m_pTradeApi->ReqQryInvestorPosition(&req, m_Config.GetNextRequestID());
        SPDLOG_INFO("req_position. nRet : {}", nRet);
    }
	return nRet == 0;
}

bool CTradeSpi::req_qry_instrument()
{
    SPDLOG_INFO("req_qry_instrument.");
	future_instruments_.clear();
    CThostFtdcQryInstrumentField req = {};
	memset(&req, 0, sizeof(CThostFtdcQryInstrumentField));

    int nTried=0;
    int nRet = m_pTradeApi->ReqQryInstrument(&req,m_Config.GetNextRequestID());
    while(nRet !=0 && nTried++<3){
        sleep(1);
        nRet = m_pTradeApi->ReqQryInstrument(&req,m_Config.GetNextRequestID());
        SPDLOG_INFO("req_qry_instrument. nRet : {}", nRet);
    }

	return nTried == 0;
}

void CTradeSpi::OnRspQryInstrument(CThostFtdcInstrumentField *pInstrument, CThostFtdcRspInfoField *pRspInfo,
                                             int nRequestID, bool bIsLast)
{
	if (pRspInfo != nullptr && pRspInfo->ErrorID != 0)
	{
		SPDLOG_ERROR("OnRspQryInstrument  ErrorID= {}, ErrorMsg= {}", pRspInfo->ErrorID, pRspInfo->ErrorMsg);
	}
	else {
        if (pInstrument->ProductClass == THOST_FTDC_PC_Futures) {
            CThostFtdcInstrumentField field = {0};
            memcpy(&field, pInstrument, sizeof(CThostFtdcInstrumentField));
            future_instruments_[field.InstrumentID] = field;
            SPDLOG_INFO("OnRspQryInstrument. InstrumentID:  {}, future_instruments_.size = {}", field.InstrumentID , future_instruments_.size());
        }

		if (bIsLast) {
			b_instruments = true;
			req_position();
		}
	}
}

void CTradeSpi::OnRspQryInvestorPosition(CThostFtdcInvestorPositionField *pInvestorPosition,
                                                   CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
{
		if (pRspInfo != nullptr && pRspInfo->ErrorID != 0)
		{
			SPDLOG_ERROR("OnRspQryInvestorPosition  ErrorID= {}, ErrorMsg= {}", pRspInfo->ErrorID, pRspInfo->ErrorMsg);
		}
		else
		{
			if (pInvestorPosition != nullptr)
			{
				CThostFtdcInvestorPositionField pos = {0};
				memcpy(&pos, pInvestorPosition, sizeof(CThostFtdcInvestorPositionField));
                SPDLOG_INFO("-----------------------------------");
                SPDLOG_INFO("OnRspQryInvestorPosition  InstrumentID= {}", pInvestorPosition->InstrumentID);
                SPDLOG_INFO("OnRspQryInvestorPosition  Position= {}", pInvestorPosition->Position);
                SPDLOG_INFO("OnRspQryInvestorPosition  YdPosition= {}", pInvestorPosition->YdPosition);

				auto& pos_map = pos.PosiDirection == THOST_FTDC_PD_Long ? long_pos_map_ : short_pos_map_;
				auto iter = pos_map.find(std::string(pos.InstrumentID));
				if (iter != pos_map.end())
				{
					pos.Position = pos.Position + iter->second.Position;
                    pos.Position = pos.YdPosition + iter->second.YdPosition;
				}
				pos_map[std::string(pInvestorPosition->InstrumentID)] = pos;
			}


			if (bIsLast)
			{
				SPDLOG_INFO("OnRspQryInvestorPosition done.");
				b_position = true;
			}
		}
}



int CTradeSpi::QueryAccount() {

    SPDLOG_INFO("QueryAccount. m_Config.GetBroker(): {}, m_Config.GetInvestorID(): {}", m_Config.GetBroker(), m_Config.GetInvestorID());
    CThostFtdcQryTradingAccountField req = {};
    strcpy(req.BrokerID, m_Config.GetBroker());
    strcpy(req.InvestorID, m_Config.GetInvestorID());
    int rtn = m_pTradeApi->ReqQryTradingAccount(&req, m_Config.GetNextRequestID());

    return rtn;
}


void CTradeSpi::OnRspQryTradingAccount(CThostFtdcTradingAccountField *pTradingAccount, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
{

}

//void CTradeSpi::OnRspQryInvestorAccount(CUstpFtdcRspInvestorAccountField *pTradingAccount, CUstpFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast){

//}

void CTradeSpi::OnRspOrderInsert(CThostFtdcInputOrderField *pOrder, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast){
	if (pRspInfo && pRspInfo->ErrorID != 0)
	{
		OnErrRtnOrderInsert(pOrder, pRspInfo);
	}
	else
	{
        SPDLOG_INFO("Insert Order Accepted.");
	}
}

void CTradeSpi::OnErrRtnOrderInsert(CThostFtdcInputOrderField *pOrder, CThostFtdcRspInfoField *pRspInfo)
{
	if (pRspInfo && pRspInfo->ErrorID != 0)
	{
		SPDLOG_ERROR("Insert Order Error. ErrorID : {}, ErrorMsg : {}", pRspInfo->ErrorID, pRspInfo->ErrorMsg);
		if (pOrder)
		{
			SPDLOG_ERROR("Error Order Info. ID : {}, LocalID : {}, Price : {}, Lots : {}", pOrder->InstrumentID, pOrder->OrderRef
				,pOrder->LimitPrice, pOrder->VolumeTotalOriginal);

			string orderId(pOrder->OrderRef);
			map<string, IPolicy*>::iterator pIter = policies.find(orderId);
			if (pIter != policies.end())
			{
				map<string, CtpOrderField*>::iterator ctpIter = ctpOrderFields.find(orderId);
				if (ctpIter != ctpOrderFields.end())
				{
					SPDLOG_ERROR("Error OrderID Found. Set Order Status : Canceled");
					CtpOrderField* field = ctpIter->second;
					field->ErrorID = pRspInfo->ErrorID;
					field->OrderStatus = THOST_FTDC_OST_Canceled;

					IPolicy* policy = pIter->second;
					policy->OnFOrderChanged(field, -1, 0, THOST_FTDC_OST_Canceled);
				}
			}
		}
	}
}

void CTradeSpi::OnRspOrderAction(CThostFtdcInputOrderActionField *pInputOrderAction, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast){
	
	if (pRspInfo && pRspInfo->ErrorID != 0)
	{
		OnErrRtnOrderAction(pInputOrderAction, pRspInfo);
	}
	else
	{
        SPDLOG_INFO("Delete Order Accepted.");
	}
}

void CTradeSpi::OnErrRtnOrderAction(CThostFtdcInputOrderActionField *pOrderAction, CThostFtdcRspInfoField *pRspInfo)
{
	if (pRspInfo && pRspInfo->ErrorID != 0)
	{
		SPDLOG_ERROR("ErrorID = {}, ErrorMsg = {}", pRspInfo->ErrorID, pRspInfo->ErrorMsg);
		string orderId(pOrderAction->OrderRef);
		map<string, IPolicy*>::iterator pIter = policies.find(orderId);
		if (pIter != policies.end())
		{
			map<string, CtpOrderField*>::iterator ctpIter = ctpOrderFields.find(orderId);
			if (ctpIter != ctpOrderFields.end())
			{
				SPDLOG_ERROR("Error OrderID Found. Set Order Status : Canceled");
				CtpOrderField* field = ctpIter->second;
				field->ErrorID = pRspInfo->ErrorID;
				field->OrderStatus = THOST_FTDC_OST_Canceled;

				IPolicy* policy = pIter->second;
				policy->OnFOrderChanged(field, -1, 0, THOST_FTDC_OST_Canceled);
			}
		}
	}
}

int CTradeSpi::CheckMyOrderID(const char * psz){

}

void CTradeSpi::OnRtnOrder(CThostFtdcOrderField *pOrder) {

    SPDLOG_INFO("OnRtnOrder called. OrderID = {} , Status = {}", pOrder->OrderRef, pOrder->OrderStatus);

	string orderId(pOrder->OrderRef);
	IPolicy* policy = NULL;
	CtpOrderField* field = NULL;

	m_OrderLock.Lock();
	map<string, IPolicy*>::iterator pIter = policies.find(orderId);
	if (pIter != policies.end())
	{
		//myinfo("OrderID found in policies.");
		policy = pIter->second;
		map<string, CtpOrderField*>::iterator ctpIter = ctpOrderFields.find(orderId);
		if (ctpIter != ctpOrderFields.end())
		{
			field = ctpIter->second;
			//myinfo("OrderID found in OrderFields.");
		}
		else
		{
            SPDLOG_INFO("OrderID is not found in OrderFields. OrderID : {}", orderId);
		}
	}
	else
	{
		SPDLOG_ERROR("OrderID is not found in Policies. OrderID : {}", orderId);
	}
	m_OrderLock.Unlock();

	if (policy == NULL)
	{
		SPDLOG_ERROR("policy is NULL");
		return;
	}
	if (field == NULL)
	{
		SPDLOG_ERROR("field is NULL");
	}

	if (policy != NULL && field != NULL)
	{
		field->OrderID = atoi(pOrder->OrderRef);
		strcpy(field->InstrumentID, pOrder->InstrumentID);
		field->OpenClose = pOrder->CombHedgeFlag[0];
		field->Direction = pOrder->Direction;
		strcpy(field->ExchangeID, pOrder->ExchangeID);
		strcpy(field->OrderSysID, pOrder->OrderSysID);
		field->OrderStatus = pOrder->OrderStatus;
		strcpy(field->InsertTime, pOrder->InsertTime);
		strcpy(field->TradingDay, pOrder->TradingDay);
		field->Price = pOrder->LimitPrice;
		field->Volume = pOrder->VolumeTotalOriginal;
		field->FilledVolume = pOrder->VolumeTraded;
		field->HedgeFlag = pOrder->CombHedgeFlag[0];
		field->OrderType = pOrder->OrderPriceType;

		char status = pOrder->OrderStatus;
		int lastTraded = field->TradedVolume;
		int thisTimeTraded = pOrder->VolumeTraded - lastTraded;
		field->TradedVolume = pOrder->VolumeTraded;
		int left = pOrder->VolumeTotal;

		if (pOrder->OrderStatus ==THOST_FTDC_OST_PartTradedNotQueueing || pOrder->OrderStatus == THOST_FTDC_OST_Canceled)
			thisTimeTraded = -1;

		policy->OnFOrderChanged(field, thisTimeTraded, left, status);

		return;
	}
	//TODO test OrderLocalID
	SPDLOG_ERROR("No Policy or Order found. ID: {}, OrderID : {}", pOrder->InstrumentID, pOrder->OrderLocalID);
}

void CTradeSpi::OnRtnTrade(CThostFtdcTradeField *pTrade){

    SPDLOG_INFO("OnRtnTrade called. OrderID = {}, TradeID = {}", pTrade->OrderRef, pTrade->TradeID);
	string orderId(pTrade->OrderLocalID);

	IPolicy* policy = NULL;
	CtpOrderField* pOrder = NULL;

	m_OrderLock.Lock();
	map<string, IPolicy*>::iterator pIter = policies.find(orderId);
	if (pIter != policies.end())
	{
		policy = pIter->second;
		map<string, CtpOrderField*>::iterator orderIter = ctpOrderFields.find(orderId);
		if (orderIter != ctpOrderFields.end())
		{
			pOrder = orderIter->second;
		}
	}
	m_OrderLock.Unlock();


	if (policy != NULL && pOrder != NULL)
	{
		TradeField* trade = new TradeField;
		memset(trade, 0, sizeof(TradeField));

		trade->OrderID = atoi(pOrder->UserOrderLocalID);
		strcpy(trade->InstrumentID, pTrade->InstrumentID);
		trade->OpenClose = pTrade->OffsetFlag;
		trade->Direction = pTrade->Direction;
		strcpy(trade->ExchangeID, pTrade->ExchangeID);
		trade->Price = pTrade->Price;
		trade->Volume = pTrade->Volume;
		trade->CurrentOrderStatus = pOrder->OrderStatus;
		trade->VolumeTotal = pOrder->Volume;
		strcpy(trade->OrderSysID, pTrade->OrderSysID);
		strcpy(trade->TradeID, pTrade->TradeID);
		strcpy(trade->TradeTime, pTrade->TradeTime);

		policy->OnFOrderFilled(pOrder, trade);

		return;
	}

	SPDLOG_ERROR("OnRtnTrade. No policy or order found. ID : {}, OrderID : {}", pTrade->InstrumentID, pTrade->OrderLocalID);
}

void CTradeSpi::OnRtnInstrumentStatus(CThostFtdcInstrumentStatusField *pInstrumentStatus){
	vector<IPolicy*>::iterator iter = v_policy.begin();
	time_t mytime = time(NULL);
	struct tm mytm;
	localtime_r(&mytime, &mytm);
	char pszTime[50];
	sprintf(pszTime, "%d%02d%02d %02d:%02d:%02d", mytm.tm_year + 1900, mytm.tm_mon + 1, mytm.tm_mday, 
		mytm.tm_hour, mytm.tm_min, mytm.tm_sec);
	for (; iter != v_policy.end(); iter++)
	{
		IPolicy* policy = *iter;
		if (policy != NULL)
		{
			policy->OnFInstrumentStatusChanged(pInstrumentStatus->ExchangeID, pszTime, pInstrumentStatus->InstrumentStatus);
		}
	}
}


void CTradeSpi::CacheTrade(CThostFtdcTradeField* pField){

}

void CTradeSpi::FlushCachedTrade(CtpOrderField* pOrder){

}

void CTradeSpi::UpdatePosition(CThostFtdcTradeField *pField){

}

const char * CTradeSpi::GetInitFile(){
	return m_cInitFile;
}

void CTradeSpi::CreateEmptyOrders(){

}

CtpOrderField* CTradeSpi::GetOrderByBrokerOID(const char* pszBrokerOrderID){
	return NULL;
}

CtpOrderField* CTradeSpi::GetNewOrder(){
	return NULL;
}

CtpOrderField* CTradeSpi::GetNewOrderNoLock(){
	return NULL;
}

CtpOrderField* CTradeSpi::GetNewOrderByID(int nOrderID){
	return NULL;
}


void CTradeSpi::GetLongPos(const char *pszIns,int& nTotal,int& nClosable,int& nTodayClosable){

	auto& pos_map = long_pos_map_;
	auto iter = pos_map.find(std::string(pszIns));
	if (iter != pos_map.end())
	{
		nTotal = iter->second.Position;
		nClosable = iter->second.Position;
		nTodayClosable = iter->second.Position - iter->second.YdPosition;
	}
}


void CTradeSpi::GetLongPos(const char *pszIns,int& nTotal,int& nClosable,int& nTodayClosable,double& fAvgOpenPrice){

	auto& pos_map = long_pos_map_;
	auto iter = pos_map.find(std::string(pszIns));
	if (iter != pos_map.end())
	{
		nTotal = iter->second.Position;
		nClosable = iter->second.Position;
		nTodayClosable = iter->second.Position - iter->second.YdPosition;
	}
}

void CTradeSpi::GetShortPos(const char *pszIns,int& nTotal,int& nClosable,int& nTodayClosable){

	auto& pos_map = short_pos_map_;
	auto iter = pos_map.find(std::string(pszIns));
	if (iter != pos_map.end())
	{
		nTotal = iter->second.Position;
		nClosable = iter->second.Position;
		nTodayClosable = iter->second.Position - iter->second.YdPosition;
	}
}

void CTradeSpi::GetShortPos(const char *pszIns,int& nTotal,int& nClosable,int& nTodayClosable,double& fAvgOpenPrice){
	auto& pos_map = short_pos_map_;
	auto iter = pos_map.find(std::string(pszIns));
	if (iter != pos_map.end())
	{
		nTotal = iter->second.Position;
		nClosable = iter->second.Position;
		nTodayClosable = iter->second.Position - iter->second.YdPosition;
	}
}


struct InstrumentField* CTradeSpi::GetInstrument(const char *pszInstrument){

}

void CTradeSpi::GetInstrument(const char* pszProductID,FutureInstrumentField** ppIns,int* pCount){

}

bool CTradeSpi::GetInstrument(FutureInstrumentField* pIns){

}


int CTradeSpi::GetMultiply(const char *pszInstrument){

}

struct CThostFtdcRspInstrumentField* CTradeSpi::GetCtpInstrument(const char *pszInstrument){

}


TradeField* CTradeSpi::GetNextTradeField(){

}




void CTradeSpi::SetBgTaskState(bool bInBg){
	m_bInBg = bInBg;
}

void CTradeSpi::OnOneSecond(){

}

void CTradeSpi::RegisterTimer(CtpOrderField* pOrder,int nAction,int nMilliSecondsAfter,IPolicy* pListener){
	// TODO: nAction == 1
	pthread_t pid;
	TimerArgs* args = new TimerArgs;
	args->millisec = nMilliSecondsAfter;
	args->policy = pListener;
	args->pOrder = pOrder;
	if (pthread_create(&pid, NULL, hook, args) != 0)
		SPDLOG_ERROR("Register Timer Error.");

	pthread_detach(pid);
}

void CTradeSpi::GetLongAvgPrice(const char *pszIns,double& fAvgOpenPrice,double& fAvgPositionPrice){

}

void CTradeSpi::GetShortAvgPrice(const char *pszIns,double& fAvgOpenPrice,double& fAvgPositionPrice){
	
}

void CTradeSpi::OnFutureMarketData(MarketDataField* pData){

}

bool CTradeSpi::IsFinished(CtpOrderField* pOrder){

}


void CTradeSpi::OnFrontConnected(){
	//ReqUserLogin();
	SPDLOG_INFO("OnFrontConected.");

	int nTried=0;
	int nRet = this->ReqAuthenticate();
	while(nRet != 0 && nTried++ < 3){
		sleep(1);
		nRet = this->ReqAuthenticate();
        SPDLOG_INFO("ReqAuthenticate  retry: {}", nTried);
	}

	m_connected = true;

}

/////////////////////////////////////////////////////////////////////
//CCtpFutureAccount Members;
/////////////////////////////////////////////////////////////////////
int CCtpFutureAccount::Login(const char* UserID,const char* Password,char* ErrorMsg,int* ErrorID){
	SPDLOG_INFO("Account Login");
	strcpy(m_cUser,UserID);
	strcpy(m_cPassword,Password);
	return m_pTradeSpi->Login(UserID,Password,ErrorMsg,ErrorID);
}

const char* CCtpFutureAccount::GetUser(){
	return m_cUser;
}

const char* CCtpFutureAccount::GetPassword(){
	return m_cPassword;
}

int CCtpFutureAccount::Logout(){
	return m_pTradeSpi->Logout();
}

IFutureTrader* CCtpFutureAccount::CreateTrader(IPolicy* pPolicy,char cHedgeFlag,bool IsSHFE){
	return m_pTradeSpi->CreateTrader(pPolicy,cHedgeFlag,IsSHFE);
}

void CCtpFutureAccount::Stop(){
	m_pTradeSpi->Stop();
}

void CCtpFutureAccount::Release(){
	m_pTradeSpi->Release();
	delete this;
}
